#include<stdio.h>
#include<unistd.h>
#include<iostream>
#include<fstream>
#include<sstream>
#include<deque>
#include<cstdlib>
#include<regex>
#include<limits.h>
#include<fcntl.h>
#include<sys/stat.h>

/*
1. 使用g++编译此cpp文件生成z.so文件
2. 在~/.cshrc中添加
    alias z '/path/z.so \!* ; cd `head -n 1 ~/.dir_history'
    bash中可使用function
        function z（）{
            /path/z.so $* ; 
            cd `head -n 1 ~/.dir_history`;
        }
3. z列出访问过的路径，使用z+num访问对应的目录，z+string列出模糊匹配访问过的目录
*/

using namespace std;
typedef deque<string> PATH_MAP;

void WriteMap(const PATH_MAP &map,const PATH_MAP &match_map);
void ReadMap(PATH_MAP &map,PATH_MAP &match_map);
void PrintMap(const PATH_MAP &map);
bool isNumber(const char* str);
string abs_path(string p);
string HOME = getenv("HOME");
PATH_MAP SortMap(const PATH_MAP &map,string p);
PATH_MAP GetMatchMap(const PATH_MAP &map,string p);

int main(int argc,char *argv[]){
    bool skip_print;
    PATH_MAP path_map,match_map;
    string p = "./";
    ReadMap(path_map,match_map);
    if(argc > 1){
        if (access(argv[1],0) == 0)
        {
            p = string(argv[1]);
            skip_print = 1;
        }
        else if (isNumber(argv[1]))
        {
            p = match_map[atoi(argv[1])];
            skip_print = 1;
        }
        else {
            match_map = GetMatchMap(path_map,argv[1]);
            if(match_map.size() == 1) p = match_map[0];
        }
    }
    else {
        match_map = path_map;
    }
    p = abs_path(p);
    path_map = SortMap(path_map,p);
    WriteMap(path_map,match_map);
    if(match_map.size()!=1 && (!skip_print))PrintMap(match_map);
}

string abs_path(string p){
    const char *c_p = p.c_str();
    char absPath[40960] = {0};
    realpath(c_p,absPath);
    return string(absPath);
}

PATH_MAP GetMatchMap(const PATH_MAP &map,string p){
    PATH_MAP match_map;
    regex pattern(".*"+p+".*");
    for(const auto & pair :map){
        if(regex_match(pair,pattern)&&access(pair.c_str(),0)==0)match_map.push_back(pair);
    }
    return match_map;
}

void PrintMap(const PATH_MAP &map){
    for (int i = 0; i < map.size(); i++)
    {
        if(i>9)break;
        cout << i << " : "<<map[i] <<endl;
    }
}

bool isNumber(const char *str){
    if(str == nullptr || *str == '\0') return false;
    while(*str){
        if(!isdigit(*str)) return false;
        ++str;
    }
    return true;
}

PATH_MAP SortMap(const PATH_MAP &map,string p){
    PATH_MAP m_map;
    char c_dir[PATH_MAX];
    getcwd(c_dir,sizeof(c_dir));
    regex pattern("("+string(c_dir)+")|("+p+")"+"?");
    for (int i = 0; i < map.size(); i++)
    {
        if(!regex_match(map[i],pattern) && i<100 && access(map[i].c_str(),0)==0)m_map.push_back(map[i]);
    }
    if(string(c_dir) != p) m_map.push_front(c_dir);
    m_map.push_front(p);
    return m_map;
}

void WriteMap(const PATH_MAP &map,const PATH_MAP &match_map){
    ofstream file(string(HOME)+"/.dir_history",fstream::out);
    if(!file.is_open()) return;
    for(const auto &pair :map){
        file << pair <<"\n";
    }
    for (int i = 0; i < match_map.size(); i++)
    {
        if(i>9) break;
        file << i << " : " << match_map[i] <<endl;
    }
    file.close();
}

void ReadMap(PATH_MAP &map,PATH_MAP &match_map){
    string line;
    fstream file;
    file.open(string(HOME)+"/.dir_history",fstream::in);
    if(!file){
        fstream new_file(string(HOME)+"/.dir_history",fstream::in);
        new_file.close();
        return;
    }
    while (getline(file,line))
    {
        regex pattern("^[0-9] : .*");
        if (regex_match(line,pattern))
        {
            match_map.push_back(line.substr(4));
        }
        else map.push_back(line);
    }
    file.close();
}